#ifdef _DEBUG
#pragma comment(lib, "../dfhack/lib/dfhack-debug.lib")
#else
#pragma comment(lib, "../dfhack/lib/dfhack.lib")
#endif

#ifdef _WIN32
#pragma comment(lib, "../irrlicht/lib/Irrlicht.lib")
#pragma comment(lib, "Psapi.lib")
#endif

#include "CRegionSceneNode.h"
#include "CTriangleSelector.h"

#include "dfize.h"

DFHack::Materials * Materials;
DFHack::memory_info *mem;
//std::vector< std::vector<std::string> > englishWords;
//std::vector< std::vector<std::string> > foreignWords;
DFHack::Creatures * Creatures = NULL;
uint32_t current_year;
uint32_t current_tick;

unsigned int update_interval = 100;
unsigned int *editbox_data = 0;

IGUIWindow *window;
IGUIWindow *window2;
ISceneNode *selectedNode = 0;
CRegionSceneNode *region = 0;
ISceneManager *smgr = 0;
ISceneNodeAnimatorCollisionResponse *collisionanim = 0;
CBlockSceneNode *block = 0;

bool keyinput[KEY_KEY_CODES_COUNT];

bool CameraActive = true;
ICameraSceneNode *cam = 0;
scene::ISceneNodeAnimatorCameraFPS *fpsanim = 0;
ICursorControl *cursor = 0;

//CTileSceneNode *SelectedTile;
CBlockSceneNode *SelectedBlock = 0;
std::vector<ISceneNode*> dwarves;
ISceneNode *CameraFollowNode = 0;
bool ShowCollisionData = false;
bool ShowAdjacentTiles = false;
bool ShowCornerTiles = false;

IGUIImage *gui_hq_image = 0;
ITexture *hq_texture = 0;

IrrlichtDevice *dev = 0;

class CEventReceiver : IEventReceiver
{
	bool OnEvent(const SEvent& event)
	{
		if(event.EventType == EET_GUI_EVENT)
		{
			switch(event.GUIEvent.EventType)
			{
			case EGET_COMBO_BOX_CHANGED:
				{
					IGUIComboBox *box = (IGUIComboBox*)event.GUIEvent.Caller;
					s32 s = box->getSelected();
					if(s != -1)
						cam->setFarValue((f32)box->getItemData(s));
					//	region->setDrawDistance((f32)box->getItemData(s));
				}
				break;
			case EGET_CHECKBOX_CHANGED:
				{
					IGUICheckBox *box = (IGUICheckBox*)event.GUIEvent.Caller;
					switch(event.GUIEvent.Caller->getID())
					{
					case 1:
						//region->setVisible(((IGUICheckBox*)event.GUIEvent.Caller)->isChecked());
						region->showTerrain(box->isChecked());
						break;
					case 2:
						ShowCollisionData = ((IGUICheckBox*)event.GUIEvent.Caller)->isChecked();
						break;
					case 3:
						region->setWireframe(((IGUICheckBox*)event.GUIEvent.Caller)->isChecked());
						break;
					case 4:
						if(box->isChecked())
						{
						//	cam->addAnimator(collisionanim);
							fpsanim->setVerticalMovement(false);
							collisionanim->setTargetNode(cam);
							collisionanim->setAnimateTarget(true);
						}
						else
						{
							//cam->removeAnimator(collisionanim);
							fpsanim->setVerticalMovement(true);
							collisionanim->setAnimateTarget(false);
						}
						break;
					case 5:
						ShowAdjacentTiles = box->isChecked();
						break;
					case 6:
						ShowCornerTiles = box->isChecked();
						break;
					case 7:
						region->setUseBatching(box->isChecked());
						break;
					}
				}
				break;
			case EGET_TREEVIEW_NODE_SELECT:
				{
#ifdef WIN32
					IGUITreeView *tree = (IGUITreeView*)event.GUIEvent.Caller;
					unsigned int *data = (unsigned int*)tree->getLastEventNode()->getData();
					editbox_data = data;
					IGUIEditBox *editbox = (IGUIEditBox*)window->getElementFromId(1);
					wchar_t buf[255];
					_itow_s(*data, buf, 255, 10);
					editbox->setText(buf);
#endif
				}
				break;
			case EGET_TREEVIEW_NODE_DESELECT:
				editbox_data = 0;
				break;
			case EGET_LISTBOX_CHANGED:
				{
					IGUIListBox *list = (IGUIListBox*)event.GUIEvent.Caller;
					s32 id = list->getSelected();
					if(id != -1)
					{
						ISceneNode *node = dwarves[id];
						if(node != 0)
						{
							CameraFollowNode = node;
							cam->setPosition(node->getAbsolutePosition());
							if(collisionanim) collisionanim->setTargetNode(cam);
						}
					}
				}
				break;
			}
		}
		else if(event.EventType == EET_KEY_INPUT_EVENT)
		{
			keyinput[event.KeyInput.Key] = event.KeyInput.PressedDown;
			if(event.KeyInput.PressedDown)
			{
				if(event.KeyInput.Key == KEY_LSHIFT)
				{
					if(fpsanim)
						fpsanim->setMoveSpeed(0.02f);
					return true;
				}
				/*if(event.KeyInput.Key == KEY_SPACE)
				{
					if(cam->isInputReceiverEnabled())
						cam->setInputReceiverEnabled(false);
					else
					{
						cursor->setPosition(0.5f, 0.5f);
						cam->setInputReceiverEnabled(true);
					}
				}*/
			}
			else
			{
				if(event.KeyInput.Key == KEY_LSHIFT)
				{
					if(fpsanim)
						fpsanim->setMoveSpeed(0.002f);
					return true;
				}
				else if(event.KeyInput.Key == KEY_SPACE)
				{
					if(cam->isInputReceiverEnabled())
					{
						cam->setInputReceiverEnabled(false);
						collisionanim->setGravity(vector3df(0,0,0));
					}
					else
					{
						cursor->setPosition(0.5f, 0.5f);
						cam->setInputReceiverEnabled(true);
						CameraFollowNode = 0;
						collisionanim->setGravity(vector3df(0,-1.0f,0));
					}
				}
				else if(event.KeyInput.Key == KEY_F2)
				{
					IVideoDriver *video = dev->getVideoDriver();
					video->beginScene();
					video->setRenderTarget(hq_texture);
					region->setHq(true);
				//	f32 dd = region->getDrawDistance();
				//	region->setDrawDistance(5000);
					f32 dd = cam->getFarValue();
					cam->setFarValue(500);
					region->setForceRecalculation(true);
					region->preRender();
//					region->render();
					smgr->drawAll();
				//	region->setDrawDistance(dd);
					cam->setFarValue(dd);
					region->setHq(false);
					region->setForceRecalculation(false);
					video->setRenderTarget(0);
					video->endScene();
					region->preRender();

					IImage *hq_image = video->createImage(hq_texture, vector2d<s32>(0,0), hq_texture->getSize());
					video->writeImageToFile(hq_image, stringc("screenshot_") + stringc(dev->getTimer()->getRealTime()) + ".jpg");
					hq_image->drop();

					gui_hq_image->setImage(hq_texture);
					gui_hq_image->setScaleImage(true);
				}
			}
		}
		/*else if(event.EventType == EET_MOUSE_INPUT_EVENT)
		{
//			if(selectedNode != 0)
			{
				if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP && cam->isInputReceiverEnabled())
				{
					ISceneCollisionManager *colman = smgr->getSceneCollisionManager();
					line3df line = colman->getRayFromScreenCoordinates(cursor->getPosition(), cam);
					CTile *tile = region->getTileFromLine(line);
					if(tile != 0)
					{
						//tile->Visible = false;
						//tile->UpdateTileType = true;
					}
					return true;
				}
			}
		}*/
		return false;
	}
};

int main(int numargs, char **args)
{
	CSettings *settingsreader = new CSettings("settings.txt", 0, "");
	SSettings settings = settingsreader->readSettings();
	delete settingsreader;

	for(int i=0; i < KEY_KEY_CODES_COUNT; i++) keyinput[i] = false;

	irr::SIrrlichtCreationParameters cp;
	cp.DriverType = EDT_OPENGL;
	cp.WindowSize = settings.Resolution;
	cp.Fullscreen = settings.Fullscreen;
	//cp.AntiAlias = 15;

	dev = createDeviceEx(cp);
	cursor = dev->getCursorControl();
	IVideoDriver *video = dev->getVideoDriver();
	smgr = dev->getSceneManager();
	ICursorControl *cursor = dev->getCursorControl();
	IGUIEnvironment *gui = dev->getGUIEnvironment();

	IEventReceiver *eventreceiver = (IEventReceiver*)new CEventReceiver();
	dev->setEventReceiver(eventreceiver);

	video->setTextureCreationFlag(ETCF_CREATE_MIP_MAPS, true);




	// Settings
	window = gui->addWindow(rect<s32>(0, 0, 125, 225), false, L"Settings");
	rect<s32> rc = window->getClientRect();

	IGUICheckBox *checkboxtiles = gui->addCheckBox(true, rect<s32>(rc.UpperLeftCorner.X, rc.UpperLeftCorner.Y, rc.LowerRightCorner.X, rc.UpperLeftCorner.Y + 20), window, 1, L"Show terrain & constructios");
	IGUICheckBox *checkboxcollision = gui->addCheckBox(false, rect<s32>(rc.UpperLeftCorner.X, rc.UpperLeftCorner.Y + 20, rc.LowerRightCorner.X, rc.UpperLeftCorner.Y + 40), window, 2, L"Show collision data");
	IGUICheckBox *checkboxwireframe = gui->addCheckBox(false, rect<s32>(rc.UpperLeftCorner.X, rc.UpperLeftCorner.Y + 40, rc.LowerRightCorner.X, rc.UpperLeftCorner.Y + 60), window, 3, L"Wireframe terrain");

	gui->addCheckBox(true, rect<s32>(rc.UpperLeftCorner.X, rc.UpperLeftCorner.Y + 70, rc.LowerRightCorner.X, rc.UpperLeftCorner.Y + 90), window, 4, L"Enable collision");
	gui->addCheckBox(false, rect<s32>(rc.UpperLeftCorner.X, rc.UpperLeftCorner.Y + 100, rc.LowerRightCorner.X, rc.UpperLeftCorner.Y + 120), window, 5, L"Highlight adjacent tiles");
	gui->addCheckBox(false, rect<s32>(rc.UpperLeftCorner.X, rc.UpperLeftCorner.Y + 120, rc.LowerRightCorner.X, rc.UpperLeftCorner.Y + 140), window, 6, L"Highlight corner tiles");
	gui->addCheckBox(true, rect<s32>(rc.UpperLeftCorner.X, rc.UpperLeftCorner.Y + 140, rc.LowerRightCorner.X, rc.UpperLeftCorner.Y + 160), window, 7, L"Use meshbuffer batching");

	gui->addStaticText(L"Draw distance", rect<s32>(rc.UpperLeftCorner.X, rc.UpperLeftCorner.Y + 170, rc.LowerRightCorner.X, rc.UpperLeftCorner.Y + 190), false, true, window);
	IGUIComboBox *combo = gui->addComboBox(rect<s32>(rc.UpperLeftCorner.X + 60, rc.UpperLeftCorner.Y + 165, rc.LowerRightCorner.X, rc.UpperLeftCorner.Y + 185), window, 8);
	combo->addItem(L"Near", 10);//50);
	combo->addItem(L"Middle", 30);//200);
	combo->addItem(L"Far", 70);//350);
	combo->addItem(L"Further", 200);//500);
	combo->setSelected(1);

	// Dwarves
	window2 = gui->addWindow(rect<s32>(0, 225, 125, 525), false, L"Dwarves");
	IGUIListBox *list = gui->addListBox(window2->getClientRect(), window2);

	// Info
	dimension2d<u32> screensize = video->getScreenSize();
	IGUIWindow *window3 = gui->addWindow(rect<s32>(screensize.Width - 125, 0, screensize.Width, 200), false, L"Info");
	rc = window3->getClientRect();
	IGUIStaticText *gui_tiletype = gui->addStaticText(L"Tile type", rect<s32>(rc.UpperLeftCorner.X, rc.UpperLeftCorner.Y + 10, rc.LowerRightCorner.X, rc.UpperLeftCorner.Y + 30), false, true, window3);
	IGUIStaticText *gui_vegtype = gui->addStaticText(L"Vegetation type", rect<s32>(rc.UpperLeftCorner.X, rc.UpperLeftCorner.Y + 30, rc.LowerRightCorner.X, rc.UpperLeftCorner.Y + 50), false, true, window3);
	IGUIStaticText *gui_material = gui->addStaticText(L"Material", rect<s32>(rc.UpperLeftCorner.X, rc.UpperLeftCorner.Y + 50, rc.LowerRightCorner.X, rc.UpperLeftCorner.Y + 70), false, true, window3);

	// HQ
	IGUIWindow *window4 = gui->addWindow(rect<s32>(screensize.Width - 125, 200, screensize.Width, 400), false, L"HQ");
	gui_hq_image = gui->addImage(window4->getClientRect(), window4);

	hq_texture = video->addRenderTargetTexture(dimension2d<u32>(1024,768), "rt", video->getColorFormat());

	//smgr->setAmbientLight(SColorf(.9f,.9f,.9f));
	smgr->setAmbientLight(SColorf(1,1,1));

	ISceneNode *skydome = smgr->addSkyDomeSceneNode(video->getTexture("data/textures/skydome.jpg"));
	ISceneNodeAnimator *anim = smgr->createRotationAnimator(vector3df(0, 0.01f, 0));
	skydome->addAnimator(anim);
	anim->drop();

	array<SKeyMap> keymap;
	SKeyMap sk;
	sk.Action = EKA_MOVE_FORWARD;
	sk.KeyCode = KEY_KEY_W;
	keymap.push_back(sk);
	sk.Action = EKA_MOVE_BACKWARD;
	sk.KeyCode = KEY_KEY_S;
	keymap.push_back(sk);
	sk.Action = EKA_STRAFE_LEFT;
	sk.KeyCode = KEY_KEY_A;
	keymap.push_back(sk);
	sk.Action = EKA_STRAFE_RIGHT;
	sk.KeyCode = KEY_KEY_D;
	keymap.push_back(sk);
	sk.Action = EKA_JUMP_UP;
	sk.KeyCode = KEY_KEY_F;
	keymap.push_back(sk);

	cam = smgr->addCameraSceneNodeFPS(0, 50.0f, 0.002f, -1, keymap.pointer(), keymap.size(), false, 0.4f);
	cam->setNearValue(0.1f);
	cam->setFarValue(30);
	core::list<ISceneNodeAnimator*> anims = cam->getAnimators();
	core::list<ISceneNodeAnimator*>::Iterator it = anims.begin();
	for(; it != anims.end(); ++it)
		if((*it)->getType() == ESNAT_CAMERA_FPS)
			fpsanim = (ISceneNodeAnimatorCameraFPS*)*it;

	std::vector<SNode> nodes;

	ILogger *logger = dev->getLogger();

	logger->log(L"DFHack", L"Initilalizing");

	DFHack::World * World;
    DFHack::ContextManager DFMgr("Memory.xml");
    DFHack::Context* DF;
    try
    {
        DF = DFMgr.getSingleContext();
        DF->Attach();
    }
    catch (.../*exception& e*/)
    {
		logger->log("DFHack", "Failed to initialize");
        getchar();
        return 1;
    }

	DFHack::Materials *Materials = DF->getMaterials();
	/*DFHack::Position *Position = DF->getPosition();

	Position->Start();
	int xx, yy, zz;
	Position->getCursorCoords(xx, yy, zz);
	printf("%d, %d, %d\n", xx, yy, zz);
	Position->Finish();*/

	DFHack::Maps *Maps = DF->getMaps();
	Maps->Start();

	uint32_t mapx, mapy, mapz;
	Maps->getSize(mapx, mapy, mapz);

	u32 maxtilex = mapx * 16;
	u32 maxtiley = mapy * 16;
	u32 maxtilez = mapz;

	logger->log(L"DFize", L"Creating region");

	region = new CRegionSceneNode(smgr->getRootSceneNode(), smgr, -1, vector3di(mapx, mapz, mapy));
	region->drop();


	if(region)
	{
		printf("DFize: Loading resources\n");
		WIN32_FIND_DATA FindFileData;
		HANDLE hFind;

		hFind = FindFirstFile(L"data/resources/*.*", &FindFileData);
		if(hFind != INVALID_HANDLE_VALUE)
		{
			do
			{
				stringw name(FindFileData.cFileName);
				if(name[0] == '.')
					continue;
				s32 i = name.findLastChar(L".", 1);
				std::string cname = stringc(name).subString(0, i).c_str();

				CSettings *settings = new CSettings((std::string("data/resources/") + stringc(FindFileData.cFileName).c_str()), 0, "");
			//	SResource *resource = settings->readResource(cname, smgr);
				irr::core::list<SResource*> resx;
				settings->readResources(smgr, resx);

				irr::core::list<SResource*>::Iterator it = resx.begin();
				for(; it != resx.end(); ++it)
					region->addResource(*it);

			//	if(resource)
			//		region->addResource(resource);
				delete settings;
			}
			while(FindNextFile(hFind, &FindFileData));
			FindClose(hFind);
		}
	}


	video::IGPUProgrammingServices* gpu = video->getGPUProgrammingServices();
	if(gpu)
	{
		printf("DFize: Loading shaders\n");
		WIN32_FIND_DATA FindFileData;
		HANDLE hFind;

		hFind = FindFirstFile(L"data/shaders/*.*", &FindFileData);
		if(hFind != INVALID_HANDLE_VALUE)
		{
			do
			{
				stringw name(FindFileData.cFileName);
				if(name[0] == '.')
					continue;
				s32 i = name.findLastChar(L".", 1);

				std::string cname = stringc(name).subString(0, i).c_str();
				if(region->getShaderMaterial(cname.c_str()))
					continue;

				std::string vert = std::string("data/shaders/") + cname + ".vert";
				std::string frag = std::string("data/shaders/") + cname + ".frag";

				const c8 *vertfile = vert.c_str();
				const c8 *fragfile = frag.c_str();

				printf("%s\n%s\n", vertfile, fragfile);				

				if(!CSettings::fileExists(vertfile))
					vertfile = 0;
				if(!CSettings::fileExists(fragfile))
					fragfile = 0;

				s32 type = gpu->addHighLevelShaderMaterialFromFiles(vertfile, "main", EVST_VS_1_1, fragfile, "main", EPST_PS_1_1);
				region->addShaderMaterial(cname, type);
			}
			while(FindNextFile(hFind, &FindFileData));
			FindClose(hFind);
		}
	}




	logger->log(L"DFize", L"Reading inorganic material colors from raw/objects/");
	int pid = DF->getProcess()->getPID();

	std::wstring folder = L"";

	std::map<std::string, DFHack::t_matgloss> matgloss_inorganic;

	HANDLE processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); 
	
	std::wstring dwarf_fortress_folder;

	if(processHandle != NULL)
	{
		TCHAR exe_path[MAX_PATH] = {0};
		if(GetModuleFileNameEx(processHandle, NULL, exe_path, sizeof(exe_path)) == 0)
		{
		//  logger->log(L"DFize", L"Failed to get Dwarf Fortress directory");
		}
		else
		{
			std::wstring folder = std::wstring(exe_path);
			int fpos = folder.find_last_of('\\');
			if(fpos != std::wstring::npos)
				folder = folder.substr(0, fpos+1);
			dwarf_fortress_folder = folder;
			wprintf(L"Dwarf fortress directory is: %s\n", folder.c_str());

			WIN32_FIND_DATA FindFileData;
			HANDLE hFind;

			hFind = FindFirstFile((folder + L"raw\\objects\\inorganic_*.txt").c_str(), &FindFileData);
			if(hFind != INVALID_HANDLE_VALUE)
			{
				do
				{
					std::string data = "";

					wprintf(L"%s\n", FindFileData.cFileName);
					std::wstring file = folder + L"raw\\objects\\" + FindFileData.cFileName;

					CSettings::readDfMaterials(stringc(file.c_str()).c_str(), "INORGANIC", matgloss_inorganic);
				}
				while(FindNextFile(hFind, &FindFileData));
				FindClose(hFind);
			}
		}
		CloseHandle(processHandle);
	}


	logger->log(L"DFize", L"Reading plant material colors from raw/objects/");

	std::map<std::string, DFHack::t_matgloss> matgloss_plant;

	WIN32_FIND_DATA FindFileData;
	HANDLE hFind;
	hFind = FindFirstFile((dwarf_fortress_folder + L"raw\\objects\\plant_*.txt").c_str(), &FindFileData);
	if(hFind != INVALID_HANDLE_VALUE)
	{
		do
		{
			std::string data = "";

			wprintf(L"%s\n", FindFileData.cFileName);
			std::wstring file = dwarf_fortress_folder + L"raw\\objects\\" + FindFileData.cFileName;
		//	wprintf(L"%s\n", file.c_str());

			CSettings::readDfMaterials(stringc(file.c_str()).c_str(), "PLANT", matgloss_plant);
		}
		while(FindNextFile(hFind, &FindFileData));
		FindClose(hFind);
	}

//	CSettings::readDfMaterials(stringc((std::wstring(dwarffortress_folder) + L"raw\\objects\\plant_standard.txt").c_str()).c_str(), "PLANT", matgloss_plant);


	logger->log(L"DFize", L"Reading materials");

	Materials->Start();
    Materials->ReadAllMaterials();


	u32 mi = 1;
	std::vector<DFHack::t_matgloss>::iterator mit = Materials->inorganic.begin();
	for(; mit != Materials->inorganic.end(); ++mit)
	{
		std::map<std::string, DFHack::t_matgloss>::iterator it = matgloss_inorganic.find((*mit).id);
		DFHack::t_matgloss mg = (*mit);
		if(it != matgloss_inorganic.end())
			mg = (*it).second;
		region->AddInorganicMaterial(new SBasicMaterial(region->readMaterial((*mit).id), video, mi, (*mit).id, mg));
		mi++;
	}

	std::vector<DFHack::t_matglossOther>::iterator omit = Materials->other.begin();
	for(; omit != Materials->other.end(); ++omit)
	{
		//printf("%d: %s\n", mi, (*omit).rawname);
		mi++;
	}

	mit = Materials->plant.begin();
	for(; mit != Materials->plant.end(); ++mit)
	{
		//printf("%d: %s %s (%d,%d,%d)\n", mi, (*mit).id, (*mit).name, (*mit).fore, (*mit).back, (*mit).bright);
		std::map<std::string, DFHack::t_matgloss>::iterator it = matgloss_plant.find((*mit).id);
		DFHack::t_matgloss mg = (*mit);
		if(it != matgloss_plant.end())
			mg = (*it).second;
		//printf("%d: %s %s (%d,%d,%d)\n", mi, (*mit).id, (*mit).name, mg.fore, mg.back, mg.bright);
		region->AddPlantMaterial(new SBasicMaterial(region->readMaterial((*mit).id), video, mi, (*mit).id, mg));
		mi++;
	}

	mi = 1;
	mit = Materials->tree.begin();
	for(; mit != Materials->tree.end(); ++mit)
	{
		//printf("%d: %s %s (%d,%d,%d)\n", mi, (*mit).id, (*mit).name, (*mit).fore, (*mit).back, (*mit).bright);
		mi++;
	}

	//mi = 1;
	mit = Materials->organic.begin();
	for(; mit != Materials->organic.end(); ++mit)
	{
		//printf("%d: %s %s (%d,%d,%d)\n", mi, (*mit).id, (*mit).name, (*mit).fore, (*mit).back, (*mit).bright);
		mi++;
	}

	logger->log(L"DFize", L"Importing tiles");

	 /**
            Method for reading the geological surrounding of the currently loaded region.
            assign is a reference to an array of nine vectors of unsigned words that are to be filled with the data
            array is indexed by the BiomeOffset enum

            I omitted resolving the layer matgloss in this API, because it would
            introduce overhead by calling some method for each tile. You have to do it
            yourself. First get the stuff from ReadGeology and then for each block get
            the RegionOffsets. For each tile get the real region from RegionOffsets and
            cross-reference it with the geology stuff (region -- array of vectors, depth --
            vector). I'm thinking about turning that Geology stuff into a
            two-dimensional array with static size.

            this is the algorithm for applying matgloss:
            void DfMap::applyGeoMatgloss(Block * b)
            {
                // load layer matgloss
                for(int x_b = 0; x_b < BLOCK_SIZE; x_b++)
                {
                    for(int y_b = 0; y_b < BLOCK_SIZE; y_b++)
                    {
                        int geolayer = b->designation[x_b][y_b].bits.geolayer_index;
                        int biome = b->designation[x_b][y_b].bits.biome;
                        b->material[x_b][y_b].type = Mat_Stone;
                        b->material[x_b][y_b].index = v_geology[b->RegionOffsets[biome]][geolayer];
                    }
                }
            }
         */

	std::vector<std::vector<uint16_t, std::allocator<char16_t>>> geology;
	Maps->ReadGeology(geology);
	for(uint32_t iy=0; iy < mapy; iy++)
	{
		for(uint32_t ix=0; ix < mapx; ix++)
		{
			bool first = true;
			for(int32_t iz=mapz-1; iz >= 0; iz--)
			{
				if(!Maps->isValidBlock(ix, iy, iz))
					continue;

			//	DFHack::tiletypes40d tiletypes;
			//	DFHack::designations40d des;
				DFHack::mapblock40d mapblock;

				if(Maps->ReadBlock40d(ix, iy, iz, &mapblock))
				{
//				bool gotdes = Maps->ReadDesignations(ix, iy, iz, &des);
	//			if(Maps->ReadTileTypes(ix, iy, iz, &tiletypes))
				//{
					CBlockSceneNode *blockscenenode = region->getBlock(ix, iz, iy);
					for(uint32_t xi = 0 ; xi < 16 ; xi++)
					{
						for(uint32_t yi = 0 ; yi < 16 ; yi++)
						{
							if(blockscenenode != 0)
							{
								u16 tt = mapblock.tiletypes[xi][yi];
								if(DFHack::isWallTerrain(tt) || DFHack::isFloorTerrain(tt) || DFHack::isRampTerrain(tt) || DFHack::isStairTerrain(tt))
								{
									u32 geolayer = mapblock.designation[xi][yi].bits.geolayer_index;
									u32 biome = mapblock.designation[xi][yi].bits.biome;
									u32 index = geology[mapblock.biome_indices[biome]][geolayer];

									blockscenenode->addTile(vector3df((f32)xi, 0, (f32)yi), tt, region->getInorganicMaterial(index));
								}

						/*		u32 building = mapblock.occupancy[xi][yi].bits.building;
								u32 unit = mapblock.occupancy[xi][yi].bits.unit;
								u32 unitgrounded = mapblock.occupancy[xi][yi].bits.unit_grounded;
								u32 item = mapblock.occupancy[xi][yi].bits.item;
								if(building || unit || unitgrounded || item)
								{
									stringw s("");
									if(building)
										s += stringw(" b:") + stringw(building);
									if(unit)
										s += stringw(" u:") + stringw(unit);
									if(unitgrounded)
										s += stringw(" ug:") + stringw(unitgrounded);
									if(item)
										s += stringw(" i:") + stringw(item);
									smgr->addBillboardTextSceneNode(0, s.c_str(), 0, dimension2df(0.5f, 0.25f), blockscenenode->getAbsolutePosition() + vector3df(((f32)xi), (f32)0, (f32)yi) + vector3df(0.5f, 0.25f, 0.5f));
								}*/
							}
						}
					}
				}
			}
		}
	}

	DFHack::Constructions *Constructions = DF->getConstructions();
	u32 constructioncount;
	Constructions->Start(constructioncount);
	printf("%d constructions\n", constructioncount);
	for(u32 ci = 0; ci < constructioncount; ci++)
	{
		DFHack::t_construction constr;
		if(Constructions->Read(ci, constr))
		{
		//	printf("%d %d %d\n", constr.x, constr.z, constr.y);
//			ISceneNode *ac = smgr->addCubeSceneNode(0.25f, 0, -1, vector3df(mapx * 16 - ((f32)constr.x) - 1, constr.z, (f32)constr.y) + vector3df(0.5f, 0.5f, 0.5f), vector3df(0,0,0), vector3df(1, 1, 1));
			//SBasicMaterial *m = region->getInorganicMaterial(constr.mat_idx);
			SBasicMaterial *m = region->getMaterial(constr.mat_idx, constr.mat_type);
//			CTile *tile = region->getTile(15-constr.x, constr.z, constr.y);
//			CBlockSceneNode *block = region->getBlock(constr.x / 16, constr.z, constr.y / 16);
			CBlockSceneNode *block = region->getBlock(constr.x / 16, constr.z, constr.y / 16);
			//Size.X - (p.X / 16) - 1, p.Y, p.Z / 16
			if(block)
	//		if(true)
			{
			//	block->setVisible(false);
				CTile *tile = block->getTile(15 - constr.x % 16, constr.y % 16);
				//CTile *tile = region->getTile(constr.x / 16 + (15 - constr.x % 16), constr.z, constr.y);
			//	if(!tile)
			//		tile = block->getTile(15 - constr.x % 16, constr.y % 16);
				if(tile)
				{
				//	printf("%d\n", tile);
				//	tile->Visible = false;
					tile->setMaterial(m);
				}
				else
					printf("notile %d %d %d\n", constr.x % 16, constr.z, constr.y % 16);
			}
			else
				printf("noblock\n");
		
	//		stringw s(stringw(m ? m->Name.c_str() : "") + " " + stringw(constr.form) + " " + stringw(constr.mat_type) + " " + stringw(mapx * 16 - (constr.x) - 1) + "," + stringw(constr.z) + "," + stringw(constr.y));
	//		ISceneNode *ac = smgr->addCubeSceneNode(0.25f, 0, -1, vector3df(mapx * 16 - ((f32)constr.x) - 1, constr.z, (f32)constr.y) + vector3df(0.5f, 0.0f, 0.5f), vector3df(0,0,0), vector3df(1, 1, 1));
	//		smgr->addBillboardTextSceneNode(0, s.c_str(), ac, dimension2df(0.5f, 0.25f), vector3df(0, 0.25f, 0));
		}
	}
	Constructions->Finish();

	Maps->Finish();

	DFHack::Buildings *Buildings = DF->getBuildings();
	u32 building_count = 0;
	Buildings->Start(building_count);

	std::map<u32, std::string> custom_workshops;
	Buildings->ReadCustomWorkshopTypes(custom_workshops);

	printf("%d buildings\n", building_count);
	for(u32 i = 0; i < building_count; i++)
	{
		DFHack::t_building building;
		if(Buildings->Read(i, building))
		{
			CBlockSceneNode *blockscenenode = region->getBlock(building.x1 / 16, building.z, building.y1 / 16);
			stringw s = /*stringw(building.x1) + "," + stringw(building.y1) + "/" + stringw(building.x2) + "," + stringw(building.y2) +*/ stringw("type:") + stringw(building.type) + " material:" + stringw(building.material.index) + " mattype:" + stringw(building.material.type);

			s32 ci = Buildings->GetCustomWorkshopType(building);
			if(ci >= 0)
				s = stringw(custom_workshops[ci].c_str());

			blockscenenode->addBuilding(vector3df((f32)(building.x1 % 16), 0, (f32)(building.y1 % 16)), building.type, region->getMaterial(building.material.index, building.material.type));//region->getInorganicMaterial(building.material.index));

//			vector3df pos = vector3df(mapx * 16 - ((f32)building.x1) - 1, (f32)building.z, (f32)building.y1) + vector3df(0.5f, 0.25f, 0.5f);
//			ISceneNode *ac = smgr->addCubeSceneNode(0.25f, 0, -1, pos/*blockscenenode->getAbsolutePosition() + vector3df((f32)(building.x1 % 16 - 1) + 0.25f, 0.25f, (f32)(building.y1 % 16) + 0.25f)*/);
//			smgr->addBillboardTextSceneNode(0, s.c_str(), ac, dimension2df(0.75f, 0.25f), vector3df(0, 0.25f, 0));
		}
	}
	Buildings->Finish();

	logger->log(L"DFize", L"Tile import done");

    Creatures = DF->getCreatures();
    World = DF->getWorld();

    current_year = World->ReadCurrentYear();
    current_tick = World->ReadCurrentTick();
    DFHack::Translation * Tran = DF->getTranslation();

    uint32_t numCreatures;
    if(!Creatures->Start(numCreatures))
    {
        std::cerr << "Can't get creatures" << std::endl;
        #ifndef LINUX_BUILD
            std::cin.ignore();
        #endif
        return 1;
    }
    if(!numCreatures)
    {
        std::cerr << "No creatures to print" << std::endl;
        #ifndef LINUX_BUILD
            std::cin.ignore();
        #endif
        return 1;
    }

    mem = DF->getMemoryInfo();

    if(!Tran->Start())
    {
        std::cerr << "Can't get name tables" << std::endl;
        return 1;
    }

	logger->log(L"DFize", L"Importing creatures");

	uint32_t i = 0;
	DFHack::t_creature temp;
	uint32_t minx, miny, minz, maxx, maxy, maxz;
	bool first = true;
	bool firstdwarf = true;

	while(i < numCreatures)
    {
        Creatures->ReadCreature(i,temp);

		if(temp.flags1.bits.dead == 0)
        {
			if(first) { first = false; minx = temp.x; miny = temp.y; minz = temp.z; maxx = temp.x; maxy = temp.y; maxz = temp.z; }

			uint32_t color = Materials->raceEx[temp.race].tilecolor.fore;
			float r = Materials->color[color].r;
			float v = Materials->color[color].v;
			float b = Materials->color[color].b;

			wchar_t name[255] = L"";
		//	if(temp.name.has_name)
			{
				std::string transName = Tran->TranslateName(temp.name,false);
				temp.name.first_name[0] = toupper(temp.name.first_name[0]);
				transName = std::string(temp.name.first_name) + " " + transName;
				mbstowcs(name, transName.c_str(), 255);
			}

			IMeshSceneNode *cube = smgr->addCubeSceneNode(0.25f, 0, temp.origin, vector3df(mapx * 16 - ((f32)temp.x) - 1, temp.z, (f32)temp.y) + vector3df(0.5f, 0.2f, 0.5f), vector3df(0,0,0), vector3df(1, 1, 1));
			//cube->setVisible(false);
			if(name[0] != 0)
			{
				IBillboardTextSceneNode *text = smgr->addBillboardTextSceneNode(0, name, cube, dimension2d<f32>(0.75f, 0.25f), vector3df(0, 0.2f, 0));
//				scene::ITextSceneNode *text = smgr->addTextSceneNode(gui->getBuiltInFont(), name, SColor(100, 255, 255, 255), cube, vector3df(0, 0.2f, 0));
	//			text->setScale(vector3df(1.5f, 1.5f, 1.5f));
			}

			SNode node;
			node.Creature = temp;
			node.Node = cube;
			nodes.push_back(node);

//			cube->setMaterialTexture(0, getTexture(SColor(255, (u32)r * 255, (u32)v * 255, (u32)b * 255), video));
			cube->setMaterialType(video::EMT_SOLID);

			if(temp.x < minx) minx = temp.x;
			if(temp.y < miny) miny = temp.y;
			if(temp.z < minz) minz = temp.z;
			if(temp.x > maxx) maxx = temp.x;
			if(temp.y > maxy) maxy = temp.y;
			if(temp.z > maxz) maxz = temp.z;

			if(Creatures->GetDwarfRaceIndex() == temp.race)
			{
				u32 i = list->addItem(name);
				dwarves.push_back(cube);
				if(firstdwarf)
				{
					firstdwarf = false;
					cam->setPosition(vector3df(mapx * 16 - ((f32)temp.x) - 1, (f32)temp.z, (f32)temp.y) + vector3df(0.25f,0.25f,0.25f));
					cam->setTarget(vector3df(mapx * 16 - ((f32)temp.x) - 1, (f32)temp.z, (f32)temp.y + 1));
				}
			}
        }
		i++;
    }

	logger->log(L"DFize", L"Creature import done");

	ITimer *timer = dev->getTimer();
	unsigned int lastupdate = timer->getRealTime();
	collisionanim = smgr->createCollisionResponseAnimator(region->getTriangleSelector(), cam, vector3df(0.15f, 0.2f, 0.15f), vector3df(0, -1.0f, 0));
	cam->addAnimator(collisionanim);
	fpsanim->setVerticalMovement(false);

	while(dev->run())
	{
		ISceneCollisionManager *colman = smgr->getSceneCollisionManager();
		
		//CTile *tile = region->getTileFromLine(colman->getRayFromScreenCoordinates(cursor->getPosition()));
		vector3df outpoint;
		triangle3df outtri;
		CTile *tile = (CTile*)colman->getSceneNodeAndCollisionPointFromRay(colman->getRayFromScreenCoordinates(cursor->getPosition()), outpoint, outtri, 0, region->getDummyNode());
	//	CTile *tile = 0;
		block = 0;
		CTile *tiles[] = { 0, 0, 0, 0,
						   0, 0, 0, 0 };
	/*	if(tile != 0)
		{
	//		tile->Lighting = false;
			vector3di p = tile->getGlobalPosition();

			if(ShowAdjacentTiles)
			{
				tiles[4] = region->getTile(p + vector3di(-1,0,0));
				tiles[5] = region->getTile(p + vector3di(1,0,0));
				tiles[6] = region->getTile(p + vector3di(0,0,-1));
				tiles[7] = region->getTile(p + vector3di(0,0,1));
			}

			if(ShowCornerTiles)
			{
				tiles[0] = region->getTile(p + vector3di(-1,0,-1));
				tiles[1] = region->getTile(p + vector3di(1,0,-1));
				tiles[2] = region->getTile(p + vector3di(1,0,1));
				tiles[3] = region->getTile(p + vector3di(-1,0,1));
			}

			for(u32 i = 0; i < 8; i++)
				if(tiles[i])
					tiles[i]->Lighting = false;
		}*/

		if(CameraFollowNode != 0)
		{
	//		CameraFollowNode->setDebugDataVisible(10);
			cam->setInputReceiverEnabled(false);
			//cam->setPosition(CameraFollowNode->getAbsolutePosition());
			vector3df target = CameraFollowNode->getAbsolutePosition();
			cam->setPosition(target - vector3df(0,0,0.5));
			cam->setTarget(target);
			/*vector3df vect = target - cam->getPosition();
			if(vect.getLengthSQ() > 1.0f)
			{
				vect.setLength(1.0f);
				cam->setPosition(target - vect);
			}*/

			/*for(unsigned int i=0; i < nodes.size(); i++)
			{
				if(nodes[i].Creature.origin == CameraFollowNode->getID())
				{
					//vector3df target = nodes[i].LastPosition;
					//target.Y = cam->getPosition().Y;
					//cam->setTarget(target);
					//cam->setRotation(cam->getRotation() + vector3df(0, 180, 0));
					cam->setPosition(nodes[i].LastPosition);
					cam->updateAbsolutePosition();
				//	printf("%f, %f, %f\n", CameraFollowNode->getAbsolutePosition().X, CameraFollowNode->getAbsolutePosition().Y, CameraFollowNode->getAbsolutePosition().Z);
					cam->setTarget(CameraFollowNode->getAbsolutePosition());
					break;
				}
			}*/

		//	if(region->isVisible())
		//		region->recalculate();
		}

		wchar_t caption[255];
		vector3di pos = tile ? tile->getGlobalPosition() : vector3di(0,0,0);
		//vector3di pos = tile ? vector3di(tile->Block->getPosition().X, tile->Block->getPosition().Y, tile->Block->getPosition().Z) : vector3di(0,0,0);
		vector3df cp = cam->getPosition();

		#ifdef WIN32
		SBasicMaterial *mate = 0;
		wchar_t matestring[255] = {0};
		if(tile)
		{
			mbstowcs(matestring, ("Resource: " + tile->Resource->Name).c_str(), 255);
			gui_vegtype->setText(matestring);

			matestring[0] = 0;
			mate = tile->getMaterial();
			if(mate)
			{
				char bb[100];
				sprintf_s(bb, 100, " %d %d %d %d", mate->Material.MaterialType, mate->MatGloss.fore, mate->MatGloss.bright, mate->MatGloss.back);
				mbstowcs(matestring, ("Material type: " + mate->Name + std::string(bb)).c_str(), 255);
			}
			gui_material->setText(matestring);
			
//			swprintf_s(matestring, L"Tile type: %d", tile->TileType);
			matestring[0] = 0;
			swprintf_s(matestring, L"Position: %d %d %d", pos.X, pos.Y, pos.Z);
			gui_tiletype->setText(matestring);
			//SColor(255, (u32)r * 255, (u32)g * 255, (u32)b * 255)
			
			//swprintf_s(matestring, L"Vegetation type: %d", tile->get);
			//gui_tiletype->setText(matestring);
		}
	//	swprintf_s(caption, 255, L"DFize - %d/%d blocks %d/%d tiles %d fps - sizetype=%d %d coltrigs", region->getVisibleBlockCount(), region->getBlockCount(), region->getCurrentHiddenTileCount(), region->getCurrentTotalTileCount(), video->getFPS(), tile ? tile->getTileSizeType() : 0, region->getTriangleSelector()->getTriangleCount());
		swprintf_s(caption, 255, L"DFize - %d fps", video->getFPS());
		#else
	//	swprintf(caption, 255, L"DFize - %d/%d blocks %d fps - sizetype=%d tiletype=%d vegtype=%d (%d, %d, %d) %s %d coltrigs", region->getVisibleBlockCount(), region->getBlockCount(), video->getFPS(), tile ? tile->getTileSizeType() : 0, tile ? tile->TileType : 0, tile ? DFHack::getVegetationType(tile->TileType) : 0, pos.X, pos.Y, pos.Z, tile ? tile->Resource->Name.c_str() : "", region->getTriangleSelector()->getTriangleCount());
		#endif
		dev->setWindowCaption(caption);

		video->beginScene();
		smgr->drawAll();

		
		if(tile)
		{
	//		video->setTransform(ETS_WORLD, region->getAbsoluteTransformation());
	//		video->setMaterial(IdentityMaterial);
			/*vector3df d = outtri.pointA - cam->getAbsolutePosition();
			vector3df e = outtri.pointA - d;
			d.setLength(d.getLength() - 0.1f);
			outtri.pointA-= e;
			outtri.pointB-= e;
			outtri.pointC-= e;*/
	//		video->draw3DTriangle(outtri);

			//printf("%s\n", colnode->getName());
			//mbstowcs(matestring, colnode->getDebugName(), 255);
			//gui_tiletype->setText(matestring);
//			video->setMaterial(IdentityMaterial);
//			video->setTransform(ETS_WORLD, tile->Block->getAbsoluteTransformation());
//			video->draw3DBox(tile->Block->getBoundingBox());
		}

		if(ShowCollisionData)
		{
			ITriangleSelector *ts = region->getTriangleSelector();//= block->getTriangleSelector();
			s32 tc = ts->getTriangleCount();
			if(tc > 0)
			{
				matrix4 m = region->getAbsoluteTransformation();
				video->setTransform(ETS_WORLD, m);
				SMaterial mat = IdentityMaterial;
				mat.EmissiveColor = SColor(50, 0, 0, 100);
				mat.setTexture(0, video->getTexture("default"));
				mat.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
				video->setMaterial(mat);
				triangle3df *t = new triangle3df[tc];
				s32 c;
				ts->getTriangles(t, tc, c);
				for(s32 i = 0; i < c; i++)
					video->draw3DTriangle(t[i]);
				delete [] t;
			}
		}

		/*video->setTransform(ETS_WORLD, matrix4());
		u32 c = region->getBlockCollectionCount();
		for(u32 i = 0; i < c; i++)
		{
			video->draw3DBox(region->getBlockCollection(i)->getBoundingBox());
		}*/

		gui->drawAll();

	/*	if(tile != 0)
		{
			tile->Lighting = true;
			for(u32 i = 0; i < 8; i++)
				if(tiles[i])
					tiles[i]->Lighting = true;
		}*/

		video->endScene();

		unsigned int thistime = timer->getRealTime();
		if(thistime - lastupdate > update_interval)
		{
			lastupdate = thistime;

			for(unsigned int i=0; i < nodes.size(); i++)
			{
				//SNode node = nodes[i];
				DFHack::t_creature temp;
				try
				{
				//	mem->getJob
					//node.Creature.current_job
					nodes[i].LastPosition = nodes[i].Node->getPosition();
					Creatures->UpdateCreature(nodes[i].Creature.origin, temp);
					//nodes[i].Node->setPosition(vector3df(temp.x, temp.z, temp.y) + vector3df(0.5f, 0.5f, 0.5f));
					nodes[i].Node->removeAnimators();
					ISceneNodeAnimator *anim = smgr->createFlyStraightAnimator(nodes[i].Node->getPosition(), vector3df(mapx * 16 - ((f32)temp.x) - 1, temp.z, temp.y) + vector3df(0.5f, 0.2f, 0.5f), update_interval * 2);
					nodes[i].Node->addAnimator(anim);
					anim->drop();
				}
				catch(...)
				{
					printf("Creature died or disappeared. Removing.\n");
					nodes[i].Node->remove();
					nodes.erase(nodes.begin() + i);
					i--;
				}

			}
		}

 		//dev->yield();
	}

	dev->closeDevice();

	Creatures->Finish();
	Materials->Finish();

    DF->Detach();

    return 0;
}
